//-----------------------------------------------------------------------------
// Filename: SIPCDR.cs
//
// Description: SIP Call Detail Records. 
// 
// History:
// 17 Feb 2008	Aaron Clauson	Created.
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2008 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Net;
using BlueFace.Sys;
using BlueFace.Sys.Net;
using log4net;

namespace BlueFace.VoIP.Net.SIP
{
    public delegate void CDRUpdatedDelegate(SIPCDR cdr);                // Used to inform CDR handlers when a CDR has been udpated.

    public enum SIPCallDirection
    {
        None = 0,
        In = 1,
        Out = 2,
    }

    /// <summary>
    /// Call detail record for a SIP call.
    /// </summary>
    public class SIPCDR
	{
        private static ILog logger = AppState.logger;

        public static event CDRUpdatedDelegate CDRUpdated;
        
        public Guid CDRId { get; private set; }
        public DateTime Created { get; private set; }
        public SIPCallDirection CallDirection { get; private set;}
        public SIPURI Destination { get; private set; }
        public SIPFromHeader From { get; private set; }
        public string CallId { get; private set; }
        public IPEndPoint LocalEndPoint { get; private set; }
        public IPEndPoint RemoteEndPoint { get; private set; }
        public bool InProgress { get; private set; }
        public int ProgressStatus { get; private set; }
        public string ProgressReasonPhrase { get; private set; }
        public DateTime ProgressTime { get; private set; }
        public bool IsAnswered { get; private set; }
        public int AnswerStatus { get; private set; }
        public string AnswerReasonPhrase { get; private set; }
        public DateTime AnswerTime { get; private set; }
        public bool IsHungup { get; private set; }
        public DateTime HangupTime { get; private set; }
        public string HangupReason { get; private set; }
        public string Owner { get; set; }
        public Guid BridgeId { get; set; }                              // If the call formed part of a bridge this will be set to the bridge id.

        public static Int64 CreatedCount;
        public static Int64 Destroyed;

        public SIPCDR(
            SIPCallDirection callDirection,
            SIPURI destination,
            SIPFromHeader from,
            string callId,
            IPEndPoint localEndPoint,
            IPEndPoint remoteEndPoint
            )
        {
            CreatedCount++;

            CDRId = Guid.NewGuid();
            Created = DateTime.Now;
            CallDirection = callDirection;
            Destination = destination;
            From = from;
            CallId = callId;
            LocalEndPoint = localEndPoint;
            RemoteEndPoint = remoteEndPoint;
            InProgress = false;
            IsAnswered = false;
            IsHungup = false;
        }

        public SIPCDR(DataRow cdrRow)
        {
            CreatedCount++;

            CDRId = new Guid(cdrRow["cdrid"] as string);
            Owner = cdrRow["owner"] as string;
            CallDirection = (cdrRow["direction"] as string == SIPCallDirection.In.ToString()) ? SIPCallDirection.In : SIPCallDirection.Out;
            Created = Convert.ToDateTime(cdrRow["created"]);
            Destination = SIPURI.ParseSIPURI(cdrRow["dsturi"] as string);
            From = SIPFromHeader.ParseFromHeader(cdrRow["from"] as string);
            CallId = cdrRow["callid"] as string;
            LocalEndPoint = (cdrRow["localsocket"] as string != null && cdrRow["localsocket"].ToString().Trim().Length > 0) ? IPSocket.GetIPEndPoint(cdrRow["localsocket"] as string) : null;
            RemoteEndPoint = (cdrRow["remotesocket"] as string != null && cdrRow["remotesocket"].ToString().Trim().Length > 0) ? IPSocket.GetIPEndPoint(cdrRow["remotesocket"] as string) : null;
            BridgeId = (cdrRow["bridgeid"] != null && cdrRow["bridgeid"].ToString().Length > 0) ? new Guid(cdrRow["bridgeid"] as string) : Guid.Empty;
            ProgressTime = (cdrRow["inprogresstime"] != null && cdrRow["inprogresstime"].ToString().Length > 0) ? Convert.ToDateTime(cdrRow["inprogresstime"]) : DateTime.MinValue;
            ProgressStatus = (cdrRow["inprogressstatus"] != null) ? Convert.ToInt32(cdrRow["inprogressstatus"]) : 0;
            ProgressReasonPhrase = cdrRow["inprogressreason"] as string;
            AnswerTime = (cdrRow["answeredtime"] != null && cdrRow["answeredtime"].ToString().Length > 0) ? Convert.ToDateTime(cdrRow["answeredtime"]) : DateTime.MinValue;
            AnswerStatus = (cdrRow["answeredstatus"] != null) ? Convert.ToInt32(cdrRow["answeredstatus"]) : 0;
            AnswerReasonPhrase = cdrRow["answeredreason"] as string;
            HangupTime = (cdrRow["hunguptime"] != null && cdrRow["hunguptime"].ToString().Length > 0) ? Convert.ToDateTime(cdrRow["hunguptime"]) : DateTime.MinValue;
            HangupReason = cdrRow["hungupreason"] as string;

            InProgress = (ProgressTime != DateTime.MinValue);
            IsAnswered = (AnswerTime != DateTime.MinValue);
            IsHungup = (HangupTime != DateTime.MinValue);
        }

        public void Progress(SIPResponseStatusCodesEnum progressStatus, string progressReason)
        {
            InProgress = true;
            ProgressTime = DateTime.Now;
            ProgressStatus = (int)progressStatus;
            ProgressReasonPhrase = progressReason;
            FireCDRUpdated(this);
        }

        public void Answered(SIPResponseStatusCodesEnum answerStatus, string answerReason)
        {
            IsAnswered = true;
            AnswerTime = DateTime.Now;
            AnswerStatus = (int)answerStatus;
            AnswerReasonPhrase = answerReason;
            FireCDRUpdated(this);
        }

        public void Hungup(string hangupReason)
        {
            IsHungup = true;
            HangupTime = DateTime.Now;
            HangupReason = hangupReason;
            FireCDRUpdated(this);
        }

        public double GetProgressDuration()
        {
            return (InProgress && IsAnswered) ? AnswerTime.Subtract(ProgressTime).TotalSeconds : 0;
        }

        public double GetAnsweredDuration()
        {
            return (IsAnswered && IsHungup) ? HangupTime.Subtract(AnswerTime).TotalSeconds : 0;
        }

        private static void FireCDRUpdated(SIPCDR cdr)
        {
            try
            {
                if (CDRUpdated != null)
                {
                    CDRUpdated(cdr);
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception FireCDRUpdated. " + excp.Message);
            }
        }

        ~SIPCDR()
        {
            Destroyed++;
        }
	}
}
